package com.example.server;

import com.alibaba.fastjson.JSON;
import com.example.game.ChessBoard;
import com.example.game.Command;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;


/**
 * @author: HanTing Wang
 * @description: WebSocketServer
 * @version:
 * @date: 2022/1/2
 */
@ServerEndpoint("/server/{clientID}")
@Component
public class WebSocketServer {

    private static final Log log = LogFactory.getLog(WebSocketServer.class);

    // 正在连接状态数
    private static int onlineCount = 0;

    private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>();

    private Session session;

    private String clientID;

    private static synchronized int getOnlineCount() {
        return onlineCount;
    }

    private static synchronized void addOnlineCount() {
        ++ WebSocketServer.onlineCount;
    }

    private static synchronized void subOnlineCount() {
        -- WebSocketServer.onlineCount;
    }

    private void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    @OnOpen
    public void onOpen(Session session, @PathParam("clientID") String clientID) {
        this.session = session;
        this.clientID = clientID;
        if (webSocketMap.containsKey(clientID)) {
            webSocketMap.remove(clientID);
            webSocketMap.put(clientID, this);
            log.info("已有用户新连接: clientID: " + clientID + " 在线数:" + getOnlineCount());
        } else {
            webSocketMap.put(clientID, this);
            addOnlineCount();
            log.info("新用户连接: clientID: " + clientID + " 在线数:" + getOnlineCount());
        }

        try {
            sendMessage(JSON.toJSONString(new HashMap<String, Object>(){
                {
                    put("type", "reply");
                    put("name", "connect");
                    put("status", true);
                    put("info", "已成功连接到服务器");
                }
            }));
        } catch (IOException ie) {
            log.error("network Error! clientID:" + clientID);
        }
    }

    @OnClose
    public void onClose() {
        try {
            if (webSocketMap.containsKey(this.partnerID)) {
                webSocketMap.get(this.partnerID).sendMessage(JSON.toJSONString(new HashMap<String, Object>() {
                    {
                        put("type", "info");
                        put("name", "error");
                        put("info", "对方下线了");
                    }
                }));
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        if(webSocketMap.containsKey(clientID)) {
            webSocketMap.remove(clientID);
            subOnlineCount();
        }
        log.info("用户退出: clientID: " + clientID + " 在线数:" + getOnlineCount());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("接收用户clientID: " + clientID + "消息 内容：[" + message + "]");
        parseCommand(message);
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.error("ERROR! clientID: "+this.clientID + " cause: " + error.getMessage());
        error.printStackTrace();
    }

    // =======================================================
    public static final int NOT_CONNECT = 0;
    public static final int CONNECTING  = 1;
    public static final int CONNECTED   = 2;

    /**
     * 找到不是自己的处于正在匹配状态的ID
     *
     * @param thisSessionID:
     *
     * @return java.lang.String
     */
    public static synchronized String getConnectingSession(String thisSessionID) {
        for (String clientID : webSocketMap.keySet()) {
            if (!clientID.equals(thisSessionID) && webSocketMap.get(clientID).connectStatus == CONNECTING) {
                return clientID;
            }
        }
        return null;
    }

    private String partnerID = null;

    private int connectStatus = NOT_CONNECT;

    private ChessBoard chessBoard = null;

    private void parseCommand(String s) {
        try {
            Command command = JSON.parseObject(s, Command.class);
            if (command.getCommandID() == null || command.getCommandID() < 0) {
                log.error("无效的指令");
                return;
            }
            log.info(command);
            switch (command.getName()) {
                case "match":
                    matchPartner();
                    break;
                case "start":
                    startSession(command);
                    break;
                case "drop":
                    dropChess(command);
                    break;
                case "cede":
                    String info = (String) command.getParams().get("info");
                    endSession("成功和局", info);
                    break;
                case "chat":
                    break;
                default:
                    break;
            }
        } catch (Exception e) {
            log.error(e);
            try {
                sendMessage(JSON.toJSONString(new HashMap<String, Object>() {
                    {
                        put("type", "reply");
                        put("status", false);
                        put("info", "出现错误:" + e.getMessage());
                    }
                }));
            } catch (IOException ie) {
                log.error(ie.getMessage());
            }
        }
    }

    /**
     * 主动匹配对手
     */
    private void matchPartner() {
        String partnerID = getConnectingSession(this.clientID);
        if (partnerID == null) {
            this.connectStatus = CONNECTING;
            try {
                sendMessage(JSON.toJSONString(new HashMap<String, Object>() {
                    {
                        put("type", "reply");
                        put("name", "match");
                        put("status", false);
                        put("info", "正在匹配");
                    }
                }));
            } catch (Exception e) {
                log.error(e.getLocalizedMessage());
            }
        } else {
            this.connectStatus = CONNECTED;
            this.partnerID = partnerID;
            webSocketMap.get(partnerID).givePartner(this.clientID);
            try {
                sendMessage(JSON.toJSONString(new HashMap<String, Object>() {
                    {
                        put("type", "reply");
                        put("name", "match");
                        put("status", true);
                        put("info", "匹配成功");
                        put("partnerID", WebSocketServer.this.partnerID);
                        put("role", "guest");
                    }
                }));
            } catch (Exception e) {
                log.error(e.getLocalizedMessage());
            }
        }
    }

    /**
     * 被动匹配对手
     *
     * @param partnerID: 对手ID
     *
     */
    private void givePartner(String partnerID) {
        this.connectStatus = CONNECTED;
        this.partnerID = partnerID;
        try {
            sendMessage(JSON.toJSONString(new HashMap<String, Object>() {
                {
                    put("type", "reply");
                    put("name", "match");
                    put("status", true);
                    put("info", "匹配成功");
                    put("partnerID", WebSocketServer.this.partnerID);
                    put("role", "host");
                }
            }));
        } catch (Exception e) {
            log.error(e.getLocalizedMessage());
        }
    }

    /**
     * 主动开局
     *
     * @param command:
     *
     */
    private void startSession(Command command) {
        this.chessBoard = new ChessBoard();
        WebSocketServer partner = webSocketMap.get(partnerID);
        if (partner == null) {
            log.error("对方退出了");
        } else {
            partner.startSession(this.chessBoard, (boolean)(command.getParams().get("isBlack")));
        }
    }

    /**
     * 被动开局
     *
     * @param chessBoard:
     * @param isBlack:
     *
     */
    private void startSession(ChessBoard chessBoard, boolean isBlack) {
        this.chessBoard = chessBoard;
        try {
            sendMessage(JSON.toJSONString(new HashMap<String, Object>(){
                {
                    put("type", "reply");
                    put("name", "start");
                    put("isBlack", isBlack);
                }
            }));
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 落子
     *
     * @param command:
     *
     */
    private void dropChess(Command command) {
        int x = (int) command.getParams().get("x");
        int y = (int) command.getParams().get("y");
        int role = (int) command.getParams().get("color");
        boolean status = this.chessBoard.dropChess(role, x, y);
        if (status) {
            String msg = JSON.toJSONString(new HashMap<String, Object>() {
                {
                    put("type", "reply");
                    put("name", "drop");
                    put("status", true);
                    put("x", x);
                    put("y", y);
                    put("isBlack", role == ChessBoard.BLACK);
                }
            });
            try {
                sendMessage(msg);
                webSocketMap.get(this.partnerID).sendMessage(msg);
            } catch (Exception e) {
                log.error(e.getMessage());
            }
            int i = chessBoard.checkWin();
            if (i != ChessBoard.BLANK) {
                if (i == ChessBoard.BLACK) {
                    String str = "黑棋胜利!";
                    endSession(str, str);
                } else {
                    String str = "白棋胜利!";
                    endSession(str, str);
                }
            }
        } else {
            try {
                sendMessage(JSON.toJSONString(new HashMap<String, Object>() {
                    {
                        put("type", "reply");
                        put("name", "drop");
                        put("status", false);
                        put("info", "请等待对方");
                    }
                }));
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
    }

    /**
     * 结束战局
     *
     * @param info1:
     *
     */
    private void endSession(String info1, String info2) {
        this.chessBoard.clearBoard();
        String msg = JSON.toJSONString(new HashMap<String, Object>() {
            {
                put("type", "reply");
                put("name", "end");
                put("info", info1);
            }
        });
        try {
            sendMessage(msg);
            msg = JSON.toJSONString(new HashMap<String, Object>() {
                {
                    put("type", "reply");
                    put("name", "end");
                    put("info", info2);
                }
            });
            webSocketMap.get(this.partnerID).sendMessage(msg);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    // =======================================================

}

